<?php
/**
 * Created by PhpStorm.
 * User: Administrator
 * Date: 2024/4/10
 * Time: 12:01
 */

namespace plugin\admin\app\logic\plan;


use app\common\lib\Common;
use app\common\lib\Guzzle;
use app\common\lib\GuzzleNew;
use app\common\lib\RedisQueue;
use app\common\lib\Spreadsheet;
use app\common\lib\TencentCos;
use app\exception\IndexException;
use app\model\douyin\CooperateAppidModel;
use plugin\admin\app\common\Constant;
use plugin\admin\app\exception\AdminException;
use plugin\admin\app\logic\BaseLogic;
use plugin\admin\app\model\image\ImageModelModel;
use plugin\admin\app\model\novel\NovelBusinessModel;
use plugin\admin\app\model\novel\NovelChapterModel;
use plugin\admin\app\model\novel\NovelChapterPartModel;
use plugin\admin\app\model\novel\NovelPlanRoleModel;
use plugin\admin\app\model\novel\NovelSpreadModel;
use plugin\admin\app\model\plan\PromotionPlanModel;
use support\Cache;
use support\Request;
use support\Response;

class PartLogic extends BaseLogic
{
    protected static $instance;

    public function __construct()
    {
        $this->model = new NovelChapterPartModel();
    }

    public static function getInstance()
    {
        //        if(is_object(self::$instance)){
//            return self::$instance;
//        }

        return new self();
    }

    public function select(Request $request): Response
    {
        $field = $request->input('field');
        if($field==false){
            $request->inputSet('field','number');
            $request->inputSet('order','asc');
        }

        $request->inputSet('isdel','0');
        return parent::select($request);
    }

    protected function formatNormal($items, $total): Response
    {
        $novelMap = $this->getListMapById(NovelSpreadModel::getInstance(),$items,'novel_sn','novel_sn');
        $cahpterMap = $this->getListMapById(NovelChapterModel::getInstance(),$items,'chapterid','id');
        $planMap = $this->getListMapById(PromotionPlanModel::getInstance(),$items,'planid','id');
        foreach($items as &$val){
            $val['title'] = isset($novelMap[$val['novel_sn']])?$novelMap[$val['novel_sn']]['title']:$val['novel_sn'];
            $val['chaptername'] = isset($cahpterMap[$val['chapterid']])?$cahpterMap[$val['chapterid']]['name']:$val['chapterid'];
            $val['planname'] = isset($planMap[$val['planid']])?$planMap[$val['planid']]['name']:$val['planid'];
            $val['subtitle'] = $val['subtitle'] ? json_decode($val['subtitle'], true) : [];

            $analyzeContentAiCommand = $val['analyze_content_ai_command'];
            $promptAiCommand = $val['prompt_ai_command'];
            $val['analyze_content_ai_command'] = !empty($analyzeContentAiCommand)?$analyzeContentAiCommand:config('param.analyze_content_ai_command');
            $val['prompt_ai_command'] = !empty($promptAiCommand)?$promptAiCommand:config('param.prompt_ai_command');

            $val['modelid'] = isset($planMap[$val['planid']])?$planMap[$val['planid']]['modelid']:0;
            $aiImageCommand = isset($planMap[$val['planid']]) && !empty($planMap[$val['planid']]['ai_image_command'])?
                                         $planMap[$val['planid']]['ai_image_command']:getAiCommand('ai_image_command_template');
            $aiImageCommands = json_decode($aiImageCommand,true);
            if($aiImageCommands){
                $aiImageCommands['seed'] = (int)($val['ai_image_command_seed']?:-1);

                $val['ai_image_command'] = json_encode($aiImageCommands,JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
            }else{
                $val['ai_image_command'] = '';
            }

            $val['ai_image_command_data'] = isset($planMap[$val['planid']]) && !empty($planMap[$val['planid']]['ai_image_command'])?
                $planMap[$val['planid']]['ai_image_command_data']:'';

            $val['ai_image_command_id'] = isset($planMap[$val['planid']]) && !empty($planMap[$val['planid']]['ai_image_command_id'])?
                $planMap[$val['planid']]['ai_image_command_id']:0;

            $roleIds = $val['roleids'] ? json_decode($val['roleids'],true):[];
            $val['roleids'] = $roleIds;
            $rolenames = NovelPlanRoleModel::getInstance()->getWhere([['id','in',$roleIds],'isdel'=>0])->pluck('name')->toArray();
            $val['rolename'] = $rolenames ? implode(',',$rolenames) : '';

        }
        return parent::formatNormal($items, $total);
    }

    public function insert(Request $request): Response
    {
        $contents = $request->input('content');
        $request->inputSet('content',json_encode($contents));

        if(is_file(runtime_path('ai_generated_part_content_'.$request->input('uninumber').'.txt'))){
            @unlink(runtime_path('ai_generated_part_content_'.$request->input('uninumber').'.txt'));
        }

        return parent::insert($request);
    }

    protected function doInsert(array $data)
    {
        $data['novel_sn'] = PromotionPlanModel::getInstance()->getWhere(['id'=>$data['planid']])->value('novel_sn');
        $data['chapterid'] = PromotionPlanModel::getInstance()->getWhere(['id'=>$data['planid']])->value('chapterid');
        $contents = json_decode($data['content'], true);
        $data['batch'] = date('YmdHis').mt_rand(10,99);

        $insert = [];
        foreach($contents as $k=>$content){
            if(empty($content)){
                continue;
            }
            $insert[] = [
                'planid'=>$data['planid'],
                'novel_sn'=>$data['novel_sn'],
                'chapterid'=>$data['chapterid'],
                'content'=>$content,
                'number'=>$k+1,
                'batch'=>$data['batch'],
            ];
        }

        $insert && NovelChapterPartModel::getInstance()->getWhere(['planid'=>$data['planid']])->update(['isdel'=>time()]);
        $insert && NovelChapterPartModel::getInstance()->insert($insert);

        return $this->json(0, 'ok', [count($contents)]);
    }

    public function update(Request $request): Response
    {
        $first = $this->model->find($request->input('id'));
        if($first==false){
            throw new IndexException(4020,'片段不存在');
        }
        if(empty($first['planid'])){
            throw new IndexException(4020,'方案不存在');
        }

        if(!empty($request->input('value'))){
            $value = $request->input('value');
            $data = array_column($value,'value');
            $request->inputSet('subtitle',json_encode($data,256));
        }

        if(!empty($request->input('roleids'))){
            $roleids = $request->input('roleids');
            $request->inputSet('roleids',json_encode(explode(',',$roleids),256));
        }

        if(!empty($request->input('image1')) || !empty($request->input('image2')) || !empty($request->input('image3'))
            || !empty($request->input('image4'))){

            foreach ([1,2,3,4] as $val){
                $key = 'image'.$val;
                $fieldKey = 'ai_image'.$val;
                $value = $request->input($key);
                $fieldValue = $request->input($fieldKey);
                if(!empty($value) && !empty($fieldValue)){
                    $request->inputSet('ai_images'.$val, $fieldValue);
                }
            }
        }

        if(!empty($request->input('modelid'))){
            $first = $this->model->find($request->input('id'));
            if(!empty($first['planid'])){
                PromotionPlanModel::getInstance()->getWhere(['id'=>$first['planid']])->update(['modelid'=>$request->input('modelid')]);
            }
        }
        if(!empty($request->input('ai_image_command'))){
            $aiImageCommand = json_decode($request->input('ai_image_command'),true);
            $aiImageCommandData['prompt'] = $aiImageCommand['prompt'] = $aiImageCommand['prompt']??'';
            $aiImageCommandData['negative_prompt'] = $aiImageCommand['negative_prompt'] = $aiImageCommand['negative_prompt']??'';
            $aiImageCommandData['sampler'] = $aiImageCommand['sampler_name'] = $aiImageCommand['sampler_name']??'';
                                             $aiImageCommand['sampler_index'] = $aiImageCommand['sampler_index']??'';
            $aiImageCommandData['scheduler'] = $aiImageCommand['scheduler'] = $aiImageCommand['scheduler']??'';
            $aiImageCommandData['steps'] = $aiImageCommand['steps'] = (int) $aiImageCommand['steps']??'';
            $aiImageCommandData['model'] = $aiImageCommand['override_settings']['sd_model_checkpoint'] = $aiImageCommand['override_settings']['sd_model_checkpoint']??'';
            $aiImageCommandData['sd_vae'] = $aiImageCommand['override_settings']['sd_vae'] = $aiImageCommand['override_settings']['sd_vae']??'';
            if(isset($aiImageCommand['alwayson_scripts'])){
                $aiImageCommandData['adetailer'] = 1;
                $aiImageCommand['alwayson_scripts']['ADetailer']['args'] = [
                    [
                        'ad_model'=>'ace_yolov8n.pt',
                        'ad_prompt'=>'',
                        'ad_confidence'=>'0.7',
                        'ad_denoising_strength'=>'0.7',
                    ]
                ];
            }else{
                $aiImageCommandData['adetailer'] = 0;
                unset($aiImageCommand['alwayson_scripts']);
            }

            $seed = $aiImageCommand['seed']??'-1';

            $aiImageCommand['seed'] = -1;
            $aiImageCommand = $aiImageCommand ? json_encode($aiImageCommand,JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) : '';

            if(strpos($aiImageCommandData['prompt'],'<lora:')!==false){
                $aiImageCommandData['rola'] = explode(':',$aiImageCommandData['prompt'])[1]??'';
            }

            $aiImageCommandData = $aiImageCommandData ? json_encode($aiImageCommandData,256) : '';
            $aiImageCommandData =str_replace([' '],'~',$aiImageCommandData);

            $update = [
                'ai_image_command'=>$aiImageCommand,
                'ai_image_command_data'=>urlencode($aiImageCommandData),
            ];
            PromotionPlanModel::getInstance()->getWhere(['id'=>$first['planid']])->update($update);

            $request->inputSet('ai_image_command_seed',$seed);
        }

        return parent::update($request);
    }

    protected function doUpdate($id, $data)
    {

        return parent::doUpdate($id, $data);
    }

    public function delete(Request $request): Response
    {
        return parent::isDelete($request);
    }

    public function doIsDelete($ids)
    {
        parent::doIsDelete($ids);

        $planid = $this->model->where(['id'=>$ids[0]])->value('planid');
        $batch = $this->model->getWhere(['planid'=>$planid,'isdel'=>0])->max('batch');
        $partNums = $this->model->getWhere(['planid'=>$planid,'isdel'=>0,'batch'=>$batch])->count();
        PromotionPlanModel::getInstance()->getWhere(['id'=>$planid])->update(['part_nums'=>$partNums]);
    }

    public function generated(Request $request): Response
    {
        if(empty($request->input('planid'))){
            throw new AdminException(4020,'方案不存在');
        }
        $planid = $request->input('planid');

        if($request->input('type')=='part'){
            $count = $this->model->getWhere(['planid'=>$planid,'isdel'=>0])->count();
            if($count){
                throw new AdminException(4020,'已经分好段,无需重复');
            }
        }

//        $chapterid = PromotionPlanModel::getInstance()->getWhere(['id'=>$request->input('planid')])->value('chapterid');
//        $content = NovelChapterModel::getInstance()->getWhere(['id'=>$chapterid])->value('content');
//        $contents = str_replace(["\n","\r",'"','“','”','[',']','【','】','「','」'],"",$content);
//        $contents = explode('@@@@',preg_replace('/@[=]+@/','@@@@',$contents));
//        return $this->json('0','ok',['content'=>$contents]);

        //生成唯一的请求id
        $requestId = md5(round(microtime(true)*1000).uniqid().mt_rand(1000,9999));

        // 队列名
        $queue = 'asyncPlanPart';
        // 数据，可以直接传数组，无需序列化
        $data = ['requestid'=>$requestId,'planid'=>$request->input('planid'),'type'=>$request->input('type')];
        // 投递消息
        RedisQueue::getInstance()->sendQueue($queue, $data);

        return $this->json('0','ok',['requestid'=>$requestId]);
    }

    public function getProgress(Request $request): Response
    {
        $requestid = $request->input('requestid');
        $cacheKey = 'async_plan_part_'.$requestid;
        $data = Cache::get($cacheKey);

        return $this->json('0','ok',['progress'=>$data['progress']??0,'error'=>$data['error']??'']);
    }

    public function generatedAnalyzeContent(Request $request): Response
    {
        if(empty($request->input('id'))){
            throw new AdminException(4020,'数据不存在');
        }

        $part = NovelChapterPartModel::getInstance()->getWhere(['id'=>$request->input('id')])->first();
        if(empty($part)){
            throw new AdminException(4020,'数据不存在');
        }

        if(empty($request->input('content'))){
            throw new AdminException(4020,'分段内容不能为空');
        }

        if(!empty($request->input('analyze_content_ai_command'))){
            $content = str_replace(['@content'],[$request->input('content')],$request->input('analyze_content_ai_command'));
        }else{
            $content = $request->input('content');
        }
        //var_dump($content);

        //todo 0 分析该段描述
        $analyzeContent = Common::generatedDescWork($content,'');

        return $this->json('0','ok',['data'=>$analyzeContent]);
    }

    public function generatedPrompt(Request $request): Response
    {
        $part = false;
        if(!empty($request->input('id'))){
            $part = NovelChapterPartModel::getInstance()->getWhere(['id'=>$request->input('id')])->first();
            if(empty($part)){
                throw new AdminException(4020,'数据不存在');
            }
        }

        if($request->input('type')=='regenerate_prompt'){
            if(empty($request->input('content'))){
                throw new AdminException(4020,'分段内容不能为空');
            }
            if(!empty($request->input('prompt_ai_command'))){
                $content = str_replace(['@content'],[$request->input('content')],$request->input('prompt_ai_command'));
            }else{
                $content = $request->input('content');
            }
        }else{
            $content = $part['content']??'';
        }
        //var_dump($content);

        //todo 1 获取描述词
        $prompt = Common::generatedImagePrompt($content);
        if($request->input('type')=='regenerate_prompt'){
            return $this->json('0','ok',['data'=>$prompt]);
        }elseif($part){
            $part->prompt = $prompt;
            $part->save();
            return $this->json('0','ok',['prompt'=>$prompt]);
        }
    }

    public function generatedImage(Request $request): Response
    {
        $part = false;
        if(!empty($request->input('id'))){
            $part = NovelChapterPartModel::getInstance()->getWhere(['id'=>$request->input('id')])->first();
            if(empty($part)){
                throw new AdminException(4020,'数据不存在');
            }
        }

//        $model = ImageModelModel::getInstance()->getWhere(['id'=>$request->input('modelid')])->first();
//
//        if(empty($model)){
//            throw new AdminException(4020,'模型必选');
//        }

        if($request->input('type')=='regenerate_image'){
            if(empty($request->input('prompt'))){
                throw new AdminException(4020,'英文描述词不能为空');
            }
            if(empty($request->input('ai_image_command'))){
                throw new AdminException(4020,'生成图像指令不能为空');
            }
            $prompt = $request->input('prompt');
        }else{
            $prompt = $part['prompt'];
        }

        if(!empty($request->input('roleids'))){
            $roleids = explode(',',$request->input('roleids'));
            $roles = [];
            is_array($roleids) && $roles = NovelPlanRoleModel::getInstance()->getWhere([['id','in',$roleids],'isdel'=>0])->get()->toArray();
            if($roles){
                $fixPrompt = '';
                $endPrompt = '';
                foreach($roles as $role){
                    $fixPrompt .=  $role['sex']==2?'1 gril,' : '1 boy,';
                    $fixPrompt .=  $role['prompt'].',';

                    $endPrompt .= ',<lora:'.$role['rola'].':1>';
                }
                $prompt = ($fixPrompt.$prompt.$endPrompt);
            }
        }

        $aiImageCommand = $request->input('ai_image_command');
        $aiImageCommands = json_decode($aiImageCommand, true);
        if($aiImageCommands==false){
            throw new AdminException(4020,'图像指令JSON格式错误');
        }
        $modelVal = $aiImageCommands['override_settings']['sd_model_checkpoint']??'';
        if($modelVal==false){
            throw new AdminException(4020,'模型不能为空');
        }

        $nums = 2;
        if(!empty($aiImageCommands['batch_size']) && is_numeric($aiImageCommands['batch_size']) && $aiImageCommands['batch_size']<=2){
            $nums = $aiImageCommands['batch_size'];
        }
        if(!empty($part['refer_image'])){
            $aiImageCommands['init_images'] = [$part['refer_image']];
            $aiImageCommand = json_encode($aiImageCommands,256);
        }
        $options = ['batch_size'=>$nums,'command_template_id'=>$request->input('ai_image_command_id',0)??0];
        if(!empty($request->input('more_people_prompt'))){
            $options['more_people_prompt'] = $request->input('more_people_prompt');
        }
        $requestId = Common::generatedAIImageByTemplate($aiImageCommand,$prompt,$modelVal,$options,'admin_plan_part_image_edit_'.($part['id']??0));

        return $this->json(0,'ok',['requestid'=>$requestId]);
    }

    public function aiRegenerate(Request $request): Response
    {
        if($request->input('type')=='regenerate_analyze_content'){
            return $this->generatedAnalyzeContent($request);
        }elseif($request->input('type')=='regenerate_prompt'){
            return $this->generatedPrompt($request);
        }elseif($request->input('type')=='regenerate_image'){
            return $this->generatedImage($request);
        }
    }

    public function aisession(Request $request): Response
    {
        $content = $request->input('question');
        if($content==false){
            throw new IndexException(4020,'提问不能为空');
        }
        $param = [
            'bot_id'=>'7389148622644281380',
            'user_id'=>(string)Common::randString(12,1),
            'stream'=>false,
            'auto_save_history'=>true,
            'additional_messages'=>[
                [
                    'role'=>'user',
                    'content'=>$content,
                    'content_type'=>'text',
                ]
            ],
        ];
        //var_dump($param);
        $headers = [
            'Authorization'=>'Bearer pat_K6YkH5ECCC6vZa7z9OgWGJGiHNZCzy4JC9lVXcb0pzQbmyJCPC5xKvO5MPguqo0X'
        ];
        $result = (new GuzzleNew())->post('https://api.coze.cn/','v3/chat',$param,$headers,'json');
        //var_dump($result);
        if(empty($result['data']['id'])){
            throw new IndexException(4020,'提问失败');
        }

        $answerContent = '';
        $n=1;
        $isfinish = false;
        while (true){
            //sleep(10);
            $param = [
                'conversation_id'=>$result['data']['conversation_id'],
                'chat_id'=>$result['data']['id'],
            ];
            $headers = [
                'Authorization'=>'Bearer pat_K6YkH5ECCC6vZa7z9OgWGJGiHNZCzy4JC9lVXcb0pzQbmyJCPC5xKvO5MPguqo0X'
            ];
            $path = 'v3/chat/message/list?'.http_build_query($param);
            $res = (new GuzzleNew())->get('https://api.coze.cn/',$path,$headers);
            if($res['code']!=0){
                throw new IndexException(4020,'回答失败');
            }

            if(!empty($res['data'])){
                foreach ($res['data'] as $val){
                    if($val['type']=='verbose' && strpos($val['content'],'generate_answer_finish')!==false){
                        $isfinish = true;
                    }
                    if($isfinish && $val['type']=='answer'){
                        $answerContent = str_replace(["\n"],['<br/>'],$val['content']);
                        break 2;
                    }
                }
            }
            if($answerContent){
                break;
            }

            if($n>120){
                throw new IndexException(4020,'回答超时');
            }
            $n++;
            usleep(200000);
        }

        return $this->json(0,'ok',['content'=>$answerContent]);
    }

    public function download(Request $request): Response
    {
        //$request->inputSet('limit',2);
        $result = $this->select($request)->rawBody();
        $listData = json_decode($result,true)['data'];

        $header = [
            'number'=>'序号',
            'content'=>'内容',
            'prompt'=>'描述词',
            'mj_keyword'=>'MJ关键词',
            'refer_image'=>'参考图(图片要放入单元格内部)',
        ];
        $data = [];
        $images = [];
        foreach ($listData as $k=>$val){
            foreach ($header as $key=>$h){
                if(isset($val[$key])){
                    if(!empty($val[$key]) && ($key=='ai_images1' || $key=='ai_images2' || $key=='refer_image')){
                        //先把远程图片下载到本地
                        $imageFile = runtime_path('spreadsheet/'.Common::getRequestId().'.png');
                        Common::mkdirs(dirname($imageFile));
                        file_put_contents($imageFile,file_get_contents($val[$key]));

                        $data[$k][$key] = $imageFile;
                        $images[] = $imageFile;
                    }else{
                        $data[$k][$key] = $val[$key]??'';
                    }
                }
            }
        }
        //var_dump($data);
        array_unshift($data,$header);

        $filePath = 'spreadsheet/part_content_download_'.$request->input('planid').'_'.time().'.xlsx';
        $filename = public_path($filePath);
        Common::mkdirs(dirname($filename));

        Spreadsheet::getInstance()->writerExcelFile($data,$filename);

        //批量删除本地图片
        foreach ($images as $img){
            @unlink($img);
        }

        return $this->json(0,'ok',['filename'=>rtrim(config('app.app_url'),'/').'/'.$filePath,]);
    }

    public function batchUpload(Request $request): Response
    {
        $planid = $request->input('planid');
        $file = current($request->file());
        if ($file && $file->isValid()) {
            $ext = strtolower($file->getUploadExtension());
            if (!in_array($ext, ['xlsx'])) {
                throw new AdminException(4020, '仅支持 xlsx格式');
            }
        }

        $data = Spreadsheet::getInstance()->readExcelFile($file->getRealPath(),['E']);
        unset($data[1]);

        $dataMap = [];
        foreach($data as $key=>$val){
            $val = array_map('trim', $val);
            $dataMap[$val[0]] = $val;
        }

        $partDatas = $this->model->getWhere(['planid'=>$planid,'isdel'=>0])->orderBy('number','asc')->get();
        if($partDatas->toArray()==false){
            throw new AdminException(4020,'请先分段');
        }

        $ids = [];
        foreach ($partDatas as $part){
            if(isset($dataMap[$part['number']])){
                $part->mj_keyword = $dataMap[$part['number']]['3']??'';
                if(!empty($dataMap[$part['number']]['4']) && is_file($dataMap[$part['number']]['4'])){
                    try{
                        $mainImageFileName = substr(md5(Common::getRequestId()),-26).'.png';
                        $res = TencentCos::getInstance()->uploadLocalFile($dataMap[$part['number']]['4'],'plan/part/referimage',$mainImageFileName);
                        if(isset($res['Location'])){
                            $part->refer_image = 'https://'.$res['Location'];
                            @unlink($dataMap[$part['number']]['4']);
                        }
                    }catch (\Exception $e){}
                }
                $part->save();
                $ids[] = $part['id'];
            }
        }
        return $this->json(0,'ok',['ids'=>$ids]);
    }

    public function uploadImage(Request $request): Response
    {
        $planid = $request->input('planid');
        $uploadImages = $request->input('upload_images');
        $data = $this->model->getWhere(['planid'=>$planid,'isdel'=>0])->orderBy('number','asc')->get();
        if($data->toArray()==false){
            throw new AdminException(4020,'请先分段');
        }

        $uploadImageMap = [];
        foreach ($uploadImages as $image){
            $name = trim(explode('.',$image['name'])[0]);
            $uploadImageMap[$name] = $image;
        }

        $exists = [];
        foreach($data as $val){
            if(in_array($val['number'],array_keys($uploadImageMap))==false){
                $exists[] = $val['number'];
            }
        }

        if($exists){
            throw new AdminException(4020,'没找到分段序号的图片有:'.implode(',',$exists));
        }

        foreach($data as $val){
            $val->refer_image = $uploadImageMap[$val['number']]['image'];
            $val->save();
        }

        return $this->json(0,'ok',[]);
    }

    public function generatedImageToImage(Request $request): Response
    {
        if(empty($request->input('planid'))){
            throw new AdminException(4020,'方案不存在');
        }

        //生成唯一的请求id
        $requestId = md5(round(microtime(true)*1000).uniqid().mt_rand(1000,9999));

        // 队列名
        $queue = 'asyncPlanPartImage';
        // 数据，可以直接传数组，无需序列化
        $data = ['requestid'=>$requestId,'planid'=>$request->input('planid')];
        // 投递消息
        RedisQueue::getInstance()->sendQueue($queue, $data);

        return $this->json('0','ok',['requestid'=>$requestId]);
    }
}